@durable-streams/client-conformance-tests 0.1.5 → 0.1.7
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 +75 -2
- package/dist/adapters/typescript-adapter.js +76 -3
- package/dist/{benchmark-runner-C_Yghc8f.js → benchmark-runner-CrE6JkbX.js} +106 -12
- package/dist/{benchmark-runner-CLAR9oLd.cjs → benchmark-runner-Db4he452.cjs} +107 -12
- package/dist/cli.cjs +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +126 -11
- package/dist/index.d.ts +126 -11
- package/dist/index.js +1 -1
- package/dist/{protocol-3cf94Xyb.d.cts → protocol-D37G3c4e.d.cts} +80 -4
- package/dist/{protocol-DyEvTHPF.d.ts → protocol-Mcbiq3nQ.d.ts} +80 -4
- package/dist/protocol.d.cts +2 -2
- package/dist/protocol.d.ts +2 -2
- package/package.json +3 -3
- package/src/adapters/typescript-adapter.ts +127 -5
- package/src/protocol.ts +85 -1
- package/src/runner.ts +202 -17
- package/src/test-cases.ts +130 -8
- package/test-cases/consumer/error-handling.yaml +42 -0
- package/test-cases/consumer/fault-injection.yaml +202 -0
- package/test-cases/consumer/offset-handling.yaml +209 -0
- package/test-cases/producer/idempotent/autoclaim.yaml +214 -0
- package/test-cases/producer/idempotent/batching.yaml +98 -0
- package/test-cases/producer/idempotent/concurrent-requests.yaml +100 -0
- package/test-cases/producer/idempotent/epoch-management.yaml +333 -0
- package/test-cases/producer/idempotent/error-handling.yaml +194 -0
- package/test-cases/producer/idempotent/multi-producer.yaml +322 -0
- package/test-cases/producer/idempotent/sequence-validation.yaml +339 -0
- package/test-cases/producer/idempotent-json-batching.yaml +134 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
id: idempotent-batching
|
|
2
|
+
name: Idempotent Producer - Batching
|
|
3
|
+
description: |
|
|
4
|
+
Tests for producer batching behavior and header interactions.
|
|
5
|
+
Batches are atomic - the entire batch shares a single sequence number.
|
|
6
|
+
category: producer
|
|
7
|
+
tags:
|
|
8
|
+
- idempotent
|
|
9
|
+
- batching
|
|
10
|
+
|
|
11
|
+
tests:
|
|
12
|
+
- id: batch-retry-deduplication
|
|
13
|
+
name: Retrying entire batch returns 204 for all
|
|
14
|
+
description: If client retries an entire JSON batch, server should deduplicate
|
|
15
|
+
setup:
|
|
16
|
+
- action: create
|
|
17
|
+
as: streamPath
|
|
18
|
+
contentType: application/json
|
|
19
|
+
operations:
|
|
20
|
+
# Send a batch of 3 items
|
|
21
|
+
- action: server-append
|
|
22
|
+
path: ${streamPath}
|
|
23
|
+
data: '[{"id":1},{"id":2},{"id":3}]'
|
|
24
|
+
producerId: batch-retry-producer
|
|
25
|
+
producerEpoch: 0
|
|
26
|
+
producerSeq: 0
|
|
27
|
+
expect:
|
|
28
|
+
status: 200
|
|
29
|
+
# Retry the exact same batch - should get 204
|
|
30
|
+
- action: server-append
|
|
31
|
+
path: ${streamPath}
|
|
32
|
+
data: '[{"id":1},{"id":2},{"id":3}]'
|
|
33
|
+
producerId: batch-retry-producer
|
|
34
|
+
producerEpoch: 0
|
|
35
|
+
producerSeq: 0
|
|
36
|
+
expect:
|
|
37
|
+
status: 204
|
|
38
|
+
duplicate: true
|
|
39
|
+
# Verify only 3 items in stream (not 6)
|
|
40
|
+
- action: read
|
|
41
|
+
path: ${streamPath}
|
|
42
|
+
expect:
|
|
43
|
+
upToDate: true
|
|
44
|
+
# Next seq should still work
|
|
45
|
+
- action: server-append
|
|
46
|
+
path: ${streamPath}
|
|
47
|
+
data: '[{"id":4}]'
|
|
48
|
+
producerId: batch-retry-producer
|
|
49
|
+
producerEpoch: 0
|
|
50
|
+
producerSeq: 1
|
|
51
|
+
expect:
|
|
52
|
+
status: 200
|
|
53
|
+
|
|
54
|
+
- id: producer-with-stream-seq
|
|
55
|
+
name: Producer headers work with Stream-Seq header
|
|
56
|
+
description: Both producer deduplication and writer coordination should work together
|
|
57
|
+
setup:
|
|
58
|
+
- action: create
|
|
59
|
+
as: streamPath
|
|
60
|
+
contentType: text/plain
|
|
61
|
+
operations:
|
|
62
|
+
- action: server-append
|
|
63
|
+
path: ${streamPath}
|
|
64
|
+
data: "msg"
|
|
65
|
+
producerId: test-producer
|
|
66
|
+
producerEpoch: 0
|
|
67
|
+
producerSeq: 0
|
|
68
|
+
headers:
|
|
69
|
+
Stream-Seq: "app-seq-001"
|
|
70
|
+
expect:
|
|
71
|
+
status: 200
|
|
72
|
+
|
|
73
|
+
- id: duplicate-json-different-payload
|
|
74
|
+
name: Duplicate JSON with different payload returns 204
|
|
75
|
+
description: Same test for JSON content type - dedup is by headers only
|
|
76
|
+
setup:
|
|
77
|
+
- action: create
|
|
78
|
+
as: streamPath
|
|
79
|
+
contentType: application/json
|
|
80
|
+
operations:
|
|
81
|
+
- action: server-append
|
|
82
|
+
path: ${streamPath}
|
|
83
|
+
data: '{"key": "original"}'
|
|
84
|
+
producerId: test-producer
|
|
85
|
+
producerEpoch: 0
|
|
86
|
+
producerSeq: 0
|
|
87
|
+
expect:
|
|
88
|
+
status: 200
|
|
89
|
+
# Retry with different JSON payload
|
|
90
|
+
- action: server-append
|
|
91
|
+
path: ${streamPath}
|
|
92
|
+
data: '{"key": "different", "extra": true}'
|
|
93
|
+
producerId: test-producer
|
|
94
|
+
producerEpoch: 0
|
|
95
|
+
producerSeq: 0
|
|
96
|
+
expect:
|
|
97
|
+
status: 204
|
|
98
|
+
duplicate: true
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
id: idempotent-concurrent-requests
|
|
2
|
+
name: Idempotent Producer - Concurrent Requests
|
|
3
|
+
description: |
|
|
4
|
+
Tests for client handling of concurrent HTTP requests (pipelining).
|
|
5
|
+
When maxInFlight > 1, requests may arrive out of order causing 409 errors.
|
|
6
|
+
The client must retry and ensure all messages are delivered.
|
|
7
|
+
category: producer
|
|
8
|
+
tags:
|
|
9
|
+
- idempotent
|
|
10
|
+
- concurrent
|
|
11
|
+
- pipelining
|
|
12
|
+
- retry
|
|
13
|
+
|
|
14
|
+
tests:
|
|
15
|
+
- id: concurrent-batches-all-delivered
|
|
16
|
+
name: All messages delivered with high concurrency
|
|
17
|
+
description: |
|
|
18
|
+
When using maxInFlight > 1, HTTP requests may arrive out of order causing 409 errors.
|
|
19
|
+
The client should retry and ensure all messages are delivered.
|
|
20
|
+
setup:
|
|
21
|
+
- action: create
|
|
22
|
+
as: streamPath
|
|
23
|
+
contentType: text/plain
|
|
24
|
+
operations:
|
|
25
|
+
# Send 20 messages with maxInFlight=5
|
|
26
|
+
# This forces concurrent batches that may arrive out of order
|
|
27
|
+
- action: idempotent-append-batch
|
|
28
|
+
path: ${streamPath}
|
|
29
|
+
producerId: test-producer
|
|
30
|
+
epoch: 0
|
|
31
|
+
maxInFlight: 5
|
|
32
|
+
items:
|
|
33
|
+
- data: "msg-00"
|
|
34
|
+
- data: "msg-01"
|
|
35
|
+
- data: "msg-02"
|
|
36
|
+
- data: "msg-03"
|
|
37
|
+
- data: "msg-04"
|
|
38
|
+
- data: "msg-05"
|
|
39
|
+
- data: "msg-06"
|
|
40
|
+
- data: "msg-07"
|
|
41
|
+
- data: "msg-08"
|
|
42
|
+
- data: "msg-09"
|
|
43
|
+
- data: "msg-10"
|
|
44
|
+
- data: "msg-11"
|
|
45
|
+
- data: "msg-12"
|
|
46
|
+
- data: "msg-13"
|
|
47
|
+
- data: "msg-14"
|
|
48
|
+
- data: "msg-15"
|
|
49
|
+
- data: "msg-16"
|
|
50
|
+
- data: "msg-17"
|
|
51
|
+
- data: "msg-18"
|
|
52
|
+
- data: "msg-19"
|
|
53
|
+
expect:
|
|
54
|
+
allSucceed: true
|
|
55
|
+
# Verify all messages are in the stream
|
|
56
|
+
- action: read
|
|
57
|
+
path: ${streamPath}
|
|
58
|
+
expect:
|
|
59
|
+
dataContainsAll:
|
|
60
|
+
- "msg-00"
|
|
61
|
+
- "msg-05"
|
|
62
|
+
- "msg-10"
|
|
63
|
+
- "msg-15"
|
|
64
|
+
- "msg-19"
|
|
65
|
+
upToDate: true
|
|
66
|
+
|
|
67
|
+
- id: concurrent-json-batches-all-delivered
|
|
68
|
+
name: All JSON messages delivered with high concurrency
|
|
69
|
+
description: Same test with JSON content type to verify JSON batching works with concurrency
|
|
70
|
+
setup:
|
|
71
|
+
- action: create
|
|
72
|
+
as: streamPath
|
|
73
|
+
contentType: application/json
|
|
74
|
+
operations:
|
|
75
|
+
- action: idempotent-append-batch
|
|
76
|
+
path: ${streamPath}
|
|
77
|
+
producerId: test-producer
|
|
78
|
+
epoch: 0
|
|
79
|
+
maxInFlight: 5
|
|
80
|
+
items:
|
|
81
|
+
- data: '{"id": 0}'
|
|
82
|
+
- data: '{"id": 1}'
|
|
83
|
+
- data: '{"id": 2}'
|
|
84
|
+
- data: '{"id": 3}'
|
|
85
|
+
- data: '{"id": 4}'
|
|
86
|
+
- data: '{"id": 5}'
|
|
87
|
+
- data: '{"id": 6}'
|
|
88
|
+
- data: '{"id": 7}'
|
|
89
|
+
- data: '{"id": 8}'
|
|
90
|
+
- data: '{"id": 9}'
|
|
91
|
+
expect:
|
|
92
|
+
allSucceed: true
|
|
93
|
+
- action: read
|
|
94
|
+
path: ${streamPath}
|
|
95
|
+
expect:
|
|
96
|
+
dataContainsAll:
|
|
97
|
+
- '"id":0'
|
|
98
|
+
- '"id":5'
|
|
99
|
+
- '"id":9'
|
|
100
|
+
upToDate: true
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
id: idempotent-epoch-management
|
|
2
|
+
name: Idempotent Producer - Epoch Management
|
|
3
|
+
description: |
|
|
4
|
+
Tests for producer epoch validation and zombie fencing.
|
|
5
|
+
Epochs enable split-brain protection - only the latest instance can write.
|
|
6
|
+
category: producer
|
|
7
|
+
tags:
|
|
8
|
+
- idempotent
|
|
9
|
+
- epoch
|
|
10
|
+
- fencing
|
|
11
|
+
|
|
12
|
+
tests:
|
|
13
|
+
- id: epoch-upgrade-accepted
|
|
14
|
+
name: Epoch upgrade resets sequence to 0
|
|
15
|
+
description: New epoch must start at seq=0 and should be accepted
|
|
16
|
+
setup:
|
|
17
|
+
- action: create
|
|
18
|
+
as: streamPath
|
|
19
|
+
contentType: text/plain
|
|
20
|
+
operations:
|
|
21
|
+
- action: server-append
|
|
22
|
+
path: ${streamPath}
|
|
23
|
+
data: "epoch0-msg0"
|
|
24
|
+
producerId: test-producer
|
|
25
|
+
producerEpoch: 0
|
|
26
|
+
producerSeq: 0
|
|
27
|
+
expect:
|
|
28
|
+
status: 200
|
|
29
|
+
- action: server-append
|
|
30
|
+
path: ${streamPath}
|
|
31
|
+
data: "epoch0-msg1"
|
|
32
|
+
producerId: test-producer
|
|
33
|
+
producerEpoch: 0
|
|
34
|
+
producerSeq: 1
|
|
35
|
+
expect:
|
|
36
|
+
status: 200
|
|
37
|
+
# Upgrade to epoch=1
|
|
38
|
+
- action: server-append
|
|
39
|
+
path: ${streamPath}
|
|
40
|
+
data: "epoch1-msg0"
|
|
41
|
+
producerId: test-producer
|
|
42
|
+
producerEpoch: 1
|
|
43
|
+
producerSeq: 0
|
|
44
|
+
expect:
|
|
45
|
+
status: 200
|
|
46
|
+
producerEpoch: 1
|
|
47
|
+
|
|
48
|
+
- id: stale-epoch-rejected
|
|
49
|
+
name: Stale epoch returns 403 (zombie fencing)
|
|
50
|
+
description: Old epoch should be rejected with 403 and current epoch in response
|
|
51
|
+
setup:
|
|
52
|
+
- action: create
|
|
53
|
+
as: streamPath
|
|
54
|
+
contentType: text/plain
|
|
55
|
+
operations:
|
|
56
|
+
# Establish epoch=1
|
|
57
|
+
- action: server-append
|
|
58
|
+
path: ${streamPath}
|
|
59
|
+
data: "msg"
|
|
60
|
+
producerId: test-producer
|
|
61
|
+
producerEpoch: 1
|
|
62
|
+
producerSeq: 0
|
|
63
|
+
expect:
|
|
64
|
+
status: 200
|
|
65
|
+
# Try to write with stale epoch=0
|
|
66
|
+
- action: server-append
|
|
67
|
+
path: ${streamPath}
|
|
68
|
+
data: "zombie"
|
|
69
|
+
producerId: test-producer
|
|
70
|
+
producerEpoch: 0
|
|
71
|
+
producerSeq: 0
|
|
72
|
+
expect:
|
|
73
|
+
status: 403
|
|
74
|
+
producerEpoch: 1
|
|
75
|
+
|
|
76
|
+
- id: epoch-rollback-rejected
|
|
77
|
+
name: Epoch rollback is rejected
|
|
78
|
+
description: Cannot go back to a lower epoch
|
|
79
|
+
setup:
|
|
80
|
+
- action: create
|
|
81
|
+
as: streamPath
|
|
82
|
+
contentType: text/plain
|
|
83
|
+
operations:
|
|
84
|
+
# Establish epoch=2
|
|
85
|
+
- action: server-append
|
|
86
|
+
path: ${streamPath}
|
|
87
|
+
data: "msg"
|
|
88
|
+
producerId: test-producer
|
|
89
|
+
producerEpoch: 2
|
|
90
|
+
producerSeq: 0
|
|
91
|
+
expect:
|
|
92
|
+
status: 200
|
|
93
|
+
# Try epoch=1 (rollback)
|
|
94
|
+
- action: server-append
|
|
95
|
+
path: ${streamPath}
|
|
96
|
+
data: "rollback"
|
|
97
|
+
producerId: test-producer
|
|
98
|
+
producerEpoch: 1
|
|
99
|
+
producerSeq: 0
|
|
100
|
+
expect:
|
|
101
|
+
status: 403
|
|
102
|
+
|
|
103
|
+
- id: epoch-increase-requires-seq-zero
|
|
104
|
+
name: Epoch increase with seq != 0 is rejected
|
|
105
|
+
description: New epoch must start at seq=0
|
|
106
|
+
setup:
|
|
107
|
+
- action: create
|
|
108
|
+
as: streamPath
|
|
109
|
+
contentType: text/plain
|
|
110
|
+
operations:
|
|
111
|
+
- action: server-append
|
|
112
|
+
path: ${streamPath}
|
|
113
|
+
data: "msg"
|
|
114
|
+
producerId: test-producer
|
|
115
|
+
producerEpoch: 0
|
|
116
|
+
producerSeq: 0
|
|
117
|
+
expect:
|
|
118
|
+
status: 200
|
|
119
|
+
# Try epoch=1 with seq=5
|
|
120
|
+
- action: server-append
|
|
121
|
+
path: ${streamPath}
|
|
122
|
+
data: "bad"
|
|
123
|
+
producerId: test-producer
|
|
124
|
+
producerEpoch: 1
|
|
125
|
+
producerSeq: 5
|
|
126
|
+
expect:
|
|
127
|
+
status: 400
|
|
128
|
+
|
|
129
|
+
- id: epoch-gap-allowed
|
|
130
|
+
name: Epoch can skip values (gap allowed)
|
|
131
|
+
description: |
|
|
132
|
+
Epochs don't need to be sequential. Jumping from epoch=0 to epoch=10
|
|
133
|
+
should be allowed (unlike sequences which must be sequential).
|
|
134
|
+
setup:
|
|
135
|
+
- action: create
|
|
136
|
+
as: streamPath
|
|
137
|
+
contentType: text/plain
|
|
138
|
+
operations:
|
|
139
|
+
- action: server-append
|
|
140
|
+
path: ${streamPath}
|
|
141
|
+
data: "epoch0"
|
|
142
|
+
producerId: test-producer
|
|
143
|
+
producerEpoch: 0
|
|
144
|
+
producerSeq: 0
|
|
145
|
+
expect:
|
|
146
|
+
status: 200
|
|
147
|
+
# Jump to epoch=10 (skipping 1-9)
|
|
148
|
+
- action: server-append
|
|
149
|
+
path: ${streamPath}
|
|
150
|
+
data: "epoch10"
|
|
151
|
+
producerId: test-producer
|
|
152
|
+
producerEpoch: 10
|
|
153
|
+
producerSeq: 0
|
|
154
|
+
expect:
|
|
155
|
+
status: 200
|
|
156
|
+
# Previous epochs now fenced
|
|
157
|
+
- action: server-append
|
|
158
|
+
path: ${streamPath}
|
|
159
|
+
data: "epoch5"
|
|
160
|
+
producerId: test-producer
|
|
161
|
+
producerEpoch: 5
|
|
162
|
+
producerSeq: 0
|
|
163
|
+
expect:
|
|
164
|
+
status: 403
|
|
165
|
+
producerEpoch: 10
|
|
166
|
+
|
|
167
|
+
- id: epoch-zero-after-higher-epoch
|
|
168
|
+
name: Epoch 0 rejected after higher epoch established
|
|
169
|
+
description: |
|
|
170
|
+
Once a higher epoch is established, epoch=0 should be rejected.
|
|
171
|
+
This is critical for zombie fencing.
|
|
172
|
+
setup:
|
|
173
|
+
- action: create
|
|
174
|
+
as: streamPath
|
|
175
|
+
contentType: text/plain
|
|
176
|
+
operations:
|
|
177
|
+
# Establish epoch=5
|
|
178
|
+
- action: server-append
|
|
179
|
+
path: ${streamPath}
|
|
180
|
+
data: "epoch5"
|
|
181
|
+
producerId: test-producer
|
|
182
|
+
producerEpoch: 5
|
|
183
|
+
producerSeq: 0
|
|
184
|
+
expect:
|
|
185
|
+
status: 200
|
|
186
|
+
# Epoch 0 should be fenced
|
|
187
|
+
- action: server-append
|
|
188
|
+
path: ${streamPath}
|
|
189
|
+
data: "epoch0-zombie"
|
|
190
|
+
producerId: test-producer
|
|
191
|
+
producerEpoch: 0
|
|
192
|
+
producerSeq: 0
|
|
193
|
+
expect:
|
|
194
|
+
status: 403
|
|
195
|
+
producerEpoch: 5
|
|
196
|
+
# Epoch 4 should also be fenced
|
|
197
|
+
- action: server-append
|
|
198
|
+
path: ${streamPath}
|
|
199
|
+
data: "epoch4-zombie"
|
|
200
|
+
producerId: test-producer
|
|
201
|
+
producerEpoch: 4
|
|
202
|
+
producerSeq: 0
|
|
203
|
+
expect:
|
|
204
|
+
status: 403
|
|
205
|
+
producerEpoch: 5
|
|
206
|
+
|
|
207
|
+
- id: split-brain-fencing
|
|
208
|
+
name: Split-brain fencing scenario
|
|
209
|
+
description: Old producer instance (zombie) should be fenced when new instance claims higher epoch
|
|
210
|
+
setup:
|
|
211
|
+
- action: create
|
|
212
|
+
as: streamPath
|
|
213
|
+
contentType: text/plain
|
|
214
|
+
operations:
|
|
215
|
+
# Producer A (original): epoch=0
|
|
216
|
+
- action: server-append
|
|
217
|
+
path: ${streamPath}
|
|
218
|
+
data: "A0"
|
|
219
|
+
producerId: shared-producer
|
|
220
|
+
producerEpoch: 0
|
|
221
|
+
producerSeq: 0
|
|
222
|
+
expect:
|
|
223
|
+
status: 200
|
|
224
|
+
# Producer B (new instance): claims epoch=1
|
|
225
|
+
- action: server-append
|
|
226
|
+
path: ${streamPath}
|
|
227
|
+
data: "B0"
|
|
228
|
+
producerId: shared-producer
|
|
229
|
+
producerEpoch: 1
|
|
230
|
+
producerSeq: 0
|
|
231
|
+
expect:
|
|
232
|
+
status: 200
|
|
233
|
+
# Producer A (zombie): tries epoch=0, seq=1 - should be fenced
|
|
234
|
+
- action: server-append
|
|
235
|
+
path: ${streamPath}
|
|
236
|
+
data: "A1"
|
|
237
|
+
producerId: shared-producer
|
|
238
|
+
producerEpoch: 0
|
|
239
|
+
producerSeq: 1
|
|
240
|
+
expect:
|
|
241
|
+
status: 403
|
|
242
|
+
producerEpoch: 1
|
|
243
|
+
|
|
244
|
+
- id: large-epoch-numbers
|
|
245
|
+
name: Large epoch numbers handled correctly
|
|
246
|
+
description: Test that large epoch values work correctly
|
|
247
|
+
setup:
|
|
248
|
+
- action: create
|
|
249
|
+
as: streamPath
|
|
250
|
+
contentType: text/plain
|
|
251
|
+
operations:
|
|
252
|
+
- action: server-append
|
|
253
|
+
path: ${streamPath}
|
|
254
|
+
data: "large-epoch"
|
|
255
|
+
producerId: test-producer
|
|
256
|
+
producerEpoch: 2147483640
|
|
257
|
+
producerSeq: 0
|
|
258
|
+
expect:
|
|
259
|
+
status: 200
|
|
260
|
+
# Stale epoch should still be rejected
|
|
261
|
+
- action: server-append
|
|
262
|
+
path: ${streamPath}
|
|
263
|
+
data: "stale"
|
|
264
|
+
producerId: test-producer
|
|
265
|
+
producerEpoch: 100
|
|
266
|
+
producerSeq: 0
|
|
267
|
+
expect:
|
|
268
|
+
status: 403
|
|
269
|
+
producerEpoch: 2147483640
|
|
270
|
+
|
|
271
|
+
- id: sequence-resets-on-epoch-upgrade
|
|
272
|
+
name: Each epoch has independent sequence space
|
|
273
|
+
description: |
|
|
274
|
+
Sequence numbers reset to 0 with each epoch upgrade.
|
|
275
|
+
The old epoch's sequences don't affect the new epoch.
|
|
276
|
+
setup:
|
|
277
|
+
- action: create
|
|
278
|
+
as: streamPath
|
|
279
|
+
contentType: text/plain
|
|
280
|
+
operations:
|
|
281
|
+
# Epoch 0: sequences 0, 1, 2
|
|
282
|
+
- action: server-append
|
|
283
|
+
path: ${streamPath}
|
|
284
|
+
data: "e0s0"
|
|
285
|
+
producerId: test-producer
|
|
286
|
+
producerEpoch: 0
|
|
287
|
+
producerSeq: 0
|
|
288
|
+
expect:
|
|
289
|
+
status: 200
|
|
290
|
+
- action: server-append
|
|
291
|
+
path: ${streamPath}
|
|
292
|
+
data: "e0s1"
|
|
293
|
+
producerId: test-producer
|
|
294
|
+
producerEpoch: 0
|
|
295
|
+
producerSeq: 1
|
|
296
|
+
expect:
|
|
297
|
+
status: 200
|
|
298
|
+
- action: server-append
|
|
299
|
+
path: ${streamPath}
|
|
300
|
+
data: "e0s2"
|
|
301
|
+
producerId: test-producer
|
|
302
|
+
producerEpoch: 0
|
|
303
|
+
producerSeq: 2
|
|
304
|
+
expect:
|
|
305
|
+
status: 200
|
|
306
|
+
# Epoch 1: starts fresh at seq=0
|
|
307
|
+
- action: server-append
|
|
308
|
+
path: ${streamPath}
|
|
309
|
+
data: "e1s0"
|
|
310
|
+
producerId: test-producer
|
|
311
|
+
producerEpoch: 1
|
|
312
|
+
producerSeq: 0
|
|
313
|
+
expect:
|
|
314
|
+
status: 200
|
|
315
|
+
- action: server-append
|
|
316
|
+
path: ${streamPath}
|
|
317
|
+
data: "e1s1"
|
|
318
|
+
producerId: test-producer
|
|
319
|
+
producerEpoch: 1
|
|
320
|
+
producerSeq: 1
|
|
321
|
+
expect:
|
|
322
|
+
status: 200
|
|
323
|
+
# Verify all 5 messages present
|
|
324
|
+
- action: read
|
|
325
|
+
path: ${streamPath}
|
|
326
|
+
expect:
|
|
327
|
+
dataContainsAll:
|
|
328
|
+
- "e0s0"
|
|
329
|
+
- "e0s1"
|
|
330
|
+
- "e0s2"
|
|
331
|
+
- "e1s0"
|
|
332
|
+
- "e1s1"
|
|
333
|
+
upToDate: true
|